//
//  IterativeServer.m
//  SchmoozingExamples
//
//  Created by garrison on Fri Apr 20 2001.
//  Copyright (c) 2001 Standard Orbit Software, LLC. All rights reserved.
//
//  Permission is granted to use this code for any purpose, at your own risk.
//  No warranties are expressed or implied.
//

/* An Algorithm for an Iterative Server [Comer93]
1) Create a socket and bind it to a well known port number for the server being offered.
2) Establish a listener on the socket for incoming connections.
3) Accept the next connection request on the socket and obtain a new socket for the connection.
4) Repeatedly read the socket for a client request, generate a response, and send the reply back according to the applicationÕs protocol.
5) When finished handling a particular client request, close the connection and go back to step 3, accepting the next connection.
*/

#import <Foundation/Foundation.h>
#import <OmniNetworking/OmniNetworking.h>
#import "Connection.h"
#import "IterativeServer.h"

@implementation IterativeServer

 - (void) run
 {
    // Implement an iterative connection processing strategy, as defined
    // by Comer's InterNetworking with TCP/IP, Vol III.  The server accepts 
    // a new connection, processes it, then closes the connection before handling 
    // the next one.  Requests will queue up on the listening socket up to the
    // the limit defined by the OS.  Darwin permits up to 128 unaccepted connection
    // requests (according to the /usr/include/sys/socket.h header).
    
    ONTCPSocket *connectionSocket = nil;
    Connection *someClient = nil;
    unsigned short connectionCount = 0;
    
    // connectionSocket holds a reference to the accepted connection.
    
    // someClient is an instance of our custom connection handling class.
    
    // connectionCount holds a count of connections accepted to
    // provide some feedback in the spartan console UI.
    
    
    // The server starts here.
    NSLog(@"Iterative Server");
    
    do {
        NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
        // Create an autorelease pool to contain memory use in the loop.
            
        NS_DURING 
        {
            NSLog(@"Listening for the next connection");
            
            connectionSocket = [listeningSocket acceptConnectionOnNewSocket];
            // Step 3.  Accept the next incoming connection.  Server execution
            // will block until a connection request is received.
            
            connectionCount += 1;
            // Increment the accepted connection count

            NSLog(@"Accepted connection %d from host %@", connectionCount, 
                        [[connectionSocket remoteAddressHost] hostname]);
            // Some console feedback
                       
            someClient = [[Connection alloc] initWithConnectedSocket:connectionSocket];
            // Create a connection object with the.
            
            [someClient processConnection];
            // Step 4. Process the connection in a server-specific way.
            
            NSLog(@"Processed connection %d.", connectionCount);
            // Process the connection.  This routine must return before the server
            // can accept and process any subsequent connections.
            
            [someClient release];
            // Step 5. Close the connection and go on to the next one.
        
            [loopPool release];
        }
        NS_HANDLER 
        {
            NSLog(@"Exception %@ raised during server processing: %@", 
                [localException name], localException);
            
            [loopPool release];
            
            break;
            // Close done the server loop if an error occurs.
        }
        NS_ENDHANDLER
        
                
    } while (1);
    // This server loops forever, accepting new connections.  As implemented here, 
    // the only way the server will stop operating is if an exception is raised 
    // during execution.  You would want to add some additional means of gracefully
    // bringing a server down (e.g. handling a SIGKILL or SIGHUP signal). 
    
 }
 
 
 
 
- (id) initWithLocalPort:(unsigned short) aPort
{
    self = [super init];
    
    if ( self )
    {
        NS_DURING {
            listeningSocket = [[ONTCPSocket tcpSocket] retain];
            // Step 1. Create an ONTCPSocket for the server.
            
            [listeningSocket startListeningOnLocalPort:aPort allowingAddressReuse:YES];
            // Step 2. Start socket listening on the server's well known port.  Allowing 
            // address reuse is helpful while debugging (it prevents a port from getting 
            // locked up if your app crashes or you terminate execution in gdb without 
            // closing the socket).
            
            NSLog(@"Listening for connections on host %@:%d", [ONHost localHostname], aPort);
        }
        NS_HANDLER {
            // Handle potential exceptions.
            NSLog(@"Server init raised %@ (%@)", [localException name], localException);
            [listeningSocket release];
            return nil;
        }
        NS_ENDHANDLER
    }
    
    return self;
}


- (void) dealloc
{
    [listeningSocket release];
    
    [super dealloc];
}


@end
